﻿using System;
using System.Reflection;
using CompactMapper.Orm.Annotations;
using CompactMapper.Orm.Enumerations;

namespace CompactMapper.Orm.UnitOfWork.Mappings.Meta.PropertyInfos.Columns
{
    public interface IColumn
    {
        String SqlType { get; }
        String Name { get; }
    }

    public abstract class Column : PropertyInfoMeta, IColumn
    {
        private readonly Boolean _isEnumerationColumn;
        private readonly PropertyInfo _enumerationToSql;
        private MethodInfo _enumerationFromSql;
        private readonly Type _underlyingType;
        private readonly String _name;
        protected Column(PropertyInfo propertyInfo)
            : base(propertyInfo)
        {
            _name = propertyInfo.Name;

            if (!typeof(Enumeration).IsAssignableFrom(propertyInfo.PropertyType)) return;
            _isEnumerationColumn = true;
            _underlyingType = propertyInfo.PropertyType;
            _enumerationToSql = _underlyingType.GetProperty("Value");
        }


        public String Name
        {
            get { return _name; }
        }

        public abstract String SqlType { get; }

        public object ToSqLiteObject(object target)
        {
            if (_isEnumerationColumn)
            {
                var enumValue = GetValue(target);
                if (enumValue != null)
                {
                    var backingValue = _enumerationToSql.GetValue(enumValue, null);
                    return backingValue;
                }
                return enumValue;
            }
            return ToSqLiteObjectNonEnumeration(target);
        }

        protected virtual object ToSqLiteObjectNonEnumeration(object target)
        {
            return GetValue(target);
        }

        public object FromSqLiteObject(object source)
        {
            if (_isEnumerationColumn)
            {
                if (source is DBNull || (_underlyingType.IsClass && source == null))
                    return null;
                if (_enumerationFromSql == null)
                {
                    var enumType = typeof(Enumeration<>).MakeGenericType(new[] { source.GetType() });
                    _enumerationFromSql = enumType.GetMethod("FromValue").MakeGenericMethod(new[] { _underlyingType });
                }
                try
                {
                    var enumVal = _enumerationFromSql.Invoke(null, new[] { source });
                    return enumVal;
                }
                catch (ApplicationException)
                {
                    return null;
                }
            }
            return FromSqLiteObjectNonEnumeration(source);
        }

        protected virtual object FromSqLiteObjectNonEnumeration(object source)
        {
            return source;
        }

        internal static Column From(PropertyInfoPersistenceAttributePair pair)
        {
            if (!(pair.PersistenceAttribute is ColumnAttribute))
            {
                throw new InvalidOperationException(String.Format("propertyInfo {0} has no column attribute",
                                                                  pair.PropertyInfo));
            }
            if (pair.PersistenceAttribute is PrimaryKeyAttribute)
            {
                return new PrimaryKeyColumn(pair);
            }
            if (pair.PersistenceAttribute is BooleanColumnAttribute)
            {
                return new BooleanColumn(pair.PropertyInfo);
            }
            if (pair.PersistenceAttribute is DateTimeColumnAttribute)
            {
                return new DateTimeColumn(pair.PropertyInfo);
            }
            if (pair.PersistenceAttribute is TextColumnAttribute)
            {
                return new TextColumn(pair);
            }
            if (pair.PersistenceAttribute is IntegerColumnAttribute)
            {
                return new IntegerColumn(pair.PropertyInfo);
            }
            if (pair.PersistenceAttribute is DoubleColumnAttribute)
            {
                return new DoubleColumn(pair.PropertyInfo);
            }
            throw new InvalidOperationException(String.Format("propertyInfo {0} not supported", pair.PropertyInfo));
        }
    }
}